package org.detailsmatter.util.assertion;

import java.lang.annotation.Annotation;
import java.util.Collection;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;

/**
 * Assert for live code that throws RuntimeException.
 * @author <a href="mailto:biethb@gmail.com">Bruno Bieth</a>
 */
public class Assert {
	private Assert() {
	}

	public static <T> T that(T actual, Matcher<? super T> matcher) {
		return that("", actual, matcher);
	}

	public static <T> T that(T actual, Matcher<? super T> matcher, String reason) {
		return that(reason, actual, matcher);
	}

	public static <T> T that(String reason, T actual, Matcher<? super T> matcher) {
		if (!matcher.matches(actual)) {
			Description description = new StringDescription();
			description.appendText(reason) //
					.appendText("\nExpected: ")// 
					.appendDescriptionOf(matcher)//
					.appendText("\n     but: ");
			matcher.describeMismatch(actual, description);

			throw new RuntimeException(description.toString());
		}
        return actual;
	}

	public static void that(boolean assertion, String reason) {
		that(reason, assertion);
	}

	public static void that(String reason, boolean assertion) {
		if (!assertion) {
			throw new RuntimeException(reason);
		}
	}

	public static <T> T notNull(T obj, String reason) {
		if (obj == null) {
			throw new NullPointerException(reason);
		}
        return obj;
	}

	public static <T> T notNull(T obj) {
		if (obj == null) {
			throw new NullPointerException();
		}
        return obj;
	}

	public static <T> Collection<T> notEmpty(Collection<T> collection) {
		notEmpty(collection, "The collection should not be empty");
        return collection;
	}

    public static <T> Collection<T> notEmpty(Collection<T> collection, String message) {
		if (collection.isEmpty()) {
			throw new RuntimeException(message);
		}
        return collection;
	}

	public static void hasAnnotation(Class<?> type, Class<? extends Annotation> annotationClass) {
		hasAnnotation(type, annotationClass, annotationClass.toString() + " is missing on " + type);
	}

	public static void hasAnnotation(Class<?> type, Class<? extends Annotation> annotationClass, String message) {
		if (type.getAnnotation(annotationClass) == null) {
			throw new RuntimeException(message);
		}
	}

	public static void hasText(String text, String message) {
		if (text == null || text.trim().isEmpty()) {
			throw new RuntimeException(message);
		}
	}

	public static void empty(Collection<?> collection) {
		empty(collection, "The collection should be empty");
	}

	public static void empty(Collection<?> collection, String message) {
		if (!collection.isEmpty()) {
			throw new RuntimeException(message);
		}
	}

	public static void maxSize(Collection<?> collection, int maxSize, String message) {
		if (collection.size() > maxSize) {
			throw new RuntimeException(message);
		}
	}

	public static void maxSize(Collection<?> collection, int maxSize) {
		maxSize(collection, maxSize, "More than " + maxSize + " elements found");
	}

	public static void isNull(Object obj, String reason) {
		if (obj != null) {
			throw new IllegalStateException(reason);
		}
	}

	public static void hasSize(Collection<?> collection, int size) {
		hasSize(collection, size, "Invalid collection size, expected " + size + " but was " + collection.size());
	}

	public static void hasSize(Collection<?> collection, int size, String message) {
		if (collection.size() != size) {
			throw new RuntimeException(message);
		}
	}

	public static void hasOneItem(Collection<?> collection) {
		hasSize(collection, 1);
	}

	public static void hasOneItem(Collection<?> collection, String message) {
		hasSize(collection, 1, message);
	}
}
